package core

import (
	"context"
	"flag"
	"fmt"
	"path/filepath"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	v1beta1 "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	"k8s.io/client-go/util/retry"
)

// @title    ListNodes
// @description   创建一个k8s的连接client
// @auth      yunsheng
// @param     无
// @return    *kubernetes.Clientset
func InitK8sClient() (*kubernetes.Clientset, error) {
	home := homedir.HomeDir()
	fmt.Println(home)

	kubeconfig := flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "kubeconfig file path")
	flag.Parse()

	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err)
	}

	clientset, err := kubernetes.NewForConfig(config)

	return clientset, err
}

// @title    ListNodes
// @description   列出集群所有node
// @auth      yunsheng
// @param     clientset	*kubernetes.Clientset
// @return     无
func ListNodes(clientset *kubernetes.Clientset) {
	timeout := int64(5)
	nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{TimeoutSeconds: &timeout})
	if err != nil {
		panic(err)
	}

	for _, v := range nodeList.Items {
		fmt.Println(v)
	}
}

// @title    ListNodes
// @description   列出集群所有node
// @auth      yunsheng
// @param     clientset	*kubernetes.Clientset
// @return     无
func CreateNamespace(clientset *kubernetes.Clientset, nsname string) {

	ns := &corev1.Namespace{
		// TypeMeta: metav1.TypeMeta{
		// 	Kind: "Namespace",
		// },
		ObjectMeta: metav1.ObjectMeta{
			Name: nsname,
		},
	}
	opts := metav1.CreateOptions{}
	ns, err := clientset.CoreV1().Namespaces().Create(context.TODO(), ns, opts)
	if err != nil {
		panic(err)
	}

	fmt.Println(ns)
}

func DeleteNamespace(clientset *kubernetes.Clientset, nsname string) {
	err := clientset.CoreV1().Namespaces().Delete(context.TODO(), nsname, metav1.DeleteOptions{})
	if err != nil {
		panic(err)
	}
}

func int32Ptr(i int32) *int32 {
	return &i
}

func CreateDeployment(clientset *kubernetes.Clientset, nsname string) {
	deploy := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name: "demo-deployment",
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: int32Ptr(2),
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"app": "demo",
				},
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": "demo",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  "web",
							Image: "nginx:1.12",
							Ports: []corev1.ContainerPort{
								{
									Name:          "http",
									Protocol:      corev1.ProtocolTCP,
									ContainerPort: 80,
								},
							},
						},
					},
				},
			},
		},
	}
	result, err := clientset.AppsV1().Deployments(nsname).Create(context.TODO(), deploy, metav1.CreateOptions{})
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}

// 升级Deployment
// 升级有两种方式:
// 1.直接修改创建的deployment，然后调用Update方法，这种会丢失create和update直接对deployment的手动修改
// 2.通过Get方法得到已经部署的deplyment，然后通过重试 Update方法，进行升级。这种可以保存中间的修改。推荐
func UpdateDeployment(clientset *kubernetes.Clientset, nsname string) {
	deployClient := clientset.AppsV1().Deployments(nsname)
	// 更新资源要用retry包起来。这也是k8s-client提供的。可以处理并发更新。
	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		result, getErr := deployClient.Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
		if getErr != nil {
			panic(fmt.Errorf("get Deployment failed: %v", getErr))
		}

		// 副本数改为1
		result.Spec.Replicas = int32Ptr(1)
		// 更新镜像版本
		result.Spec.Template.Spec.Containers[0].Image = "nginx:1.13"
		_, updateErr := deployClient.Update(context.TODO(), result, metav1.UpdateOptions{})
		return updateErr
	})

	if retryErr != nil {
		panic(fmt.Errorf("update failed: %v", retryErr))
	}
}

func ListDeployment(clientset *kubernetes.Clientset, nsname string) {
	deployClient := clientset.AppsV1().Deployments(nsname)
	list, err := deployClient.List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		panic(err)
	}
	for _, d := range list.Items {
		fmt.Printf(" * %s (%d replicas)\n", d.Name, *d.Spec.Replicas)
	}

}

func DeleteDeployment(clientset *kubernetes.Clientset, nsname string) {
	deployClient := clientset.AppsV1().Deployments(nsname)
	deletePolicy := metav1.DeletePropagationForeground
	if delerr := deployClient.Delete(context.TODO(), "demo-deployment", metav1.DeleteOptions{
		PropagationPolicy: &deletePolicy,
	}); delerr != nil {
		panic(delerr)
	}

	fmt.Printf("deleted deployment: %v", nsname)

}

func CreateIngress(clientset *kubernetes.Clientset, name string) {

	ingress := &v1beta1.Ingress{}

	opts := metav1.CreateOptions{}

	clientset.ExtensionsV1beta1().Ingresses(name).Create(context.TODO(), ingress, opts)
}
